package com.xingchi.tornado.storage.core.supports;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.xingchi.tornado.core.exception.ExceptionWrap;
import com.xingchi.tornado.storage.core.AbstractFileStorageService;
import com.xingchi.tornado.storage.core.config.StorageProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Date;

import static com.xingchi.tornado.storage.core.exception.FileCode.FILE_UPLOAD_FAIL;

/**
 * 阿里云oss文件存储服务
 *
 * @author xingchi
 * @date 2024/4/30 0:04
 */
@Slf4j
public class OSSFileStorageService extends AbstractFileStorageService {

    private final String bucketName;
    private final OSS oss;
    private final StorageProperties.OSSProperties ossProperties;

    public OSSFileStorageService(StorageProperties.OSSProperties ossProperties) {
        // oss属性校验
        this.ossCheck(ossProperties);

        this.bucketName = ossProperties.getBucketName();
        this.ossProperties = ossProperties;
        this.oss = new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKey(), ossProperties.getSecretKey());

        if (ossProperties.isAuto()) {
            oss.createBucket(bucketName);
            log.info("bucket not exists auto create bucket: '{}'", bucketName);
        } else if (!oss.doesBucketExist(bucketName)) {
            throw new RuntimeException("bucket not exists, please create bucket first");
        }
        log.info("aliyun oss file storage service init successfully!");
    }

    private void ossCheck(StorageProperties.OSSProperties ossProperties) {
        if (StringUtils.isBlank(ossProperties.getEndpoint())) {
            throw new RuntimeException("aliyun oss endpoint is blank!");
        }

        if (StringUtils.isBlank(ossProperties.getAccessKey()) || StringUtils.isBlank(ossProperties.getSecretKey())) {
            throw new RuntimeException("aliyun oss accessKey or secretKey is empty!");
        }

        if (StringUtils.isBlank(ossProperties.getBucketName())) {
            throw new RuntimeException("aliyun oss bucketName is empty!");
        }
    }

    private String replacePath(String path) {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        return path;
    }

    /**
     * 根据文件的输入流上传文件
     *
     * @param is       文件的输入流
     * @param fileName 文件的完整名称
     * @return 文件在文件存储服务器上的路径唯一标识，在云存储中返回文件的对象名
     */
    @Override
    public String uploadFile(InputStream is, String fileName) {
        String path = getPath(fileName);

        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentDisposition("attachment; filename*=UTF-8''" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20"));
            oss.putObject(bucketName, replacePath(path), is, metadata);
        } catch (OSSException | ClientException | UnsupportedEncodingException e) {
            ExceptionWrap.cast(FILE_UPLOAD_FAIL, e.getMessage());
        }

        return path;
    }

    /**
     * 根据文件在文件服务器上的存储路径获取文件对应的字节书，注意文件过大可能会导致内存溢出问题
     *
     * @param path 文件的存储路径
     * @return 文件字节
     */
    @Override
    public byte[] getFileBytes(String path) {
        try {
            return IOUtils.toByteArray(this.getInputStream(replacePath(path)));
        } catch (IOException e) {
            log.warn("get file bytes error: {}", e.getMessage());
            return new byte[0];
        }
    }

    @Override
    public InputStream getInputStream(String path){
        OSSObject object = oss.getObject(bucketName, replacePath(path));
        return object.getObjectContent();
    }

    /**
     * 删除文件
     *
     * @param path 文件的存储路径
     */
    @Override
    public void deleteFile(String path) {
        oss.deleteObject(bucketName, replacePath(path));
    }

    /**
     * 获取文件的访问url
     *
     * @param path 文件的存储路径，在云存储中则为对应的对象名称
     * @return url
     */
    @Override
    public String getUrl(String path) {
        Duration duration = ossProperties.getExpireTime();
        long millis = duration.toMillis();
        Date expireTime = new Date(System.currentTimeMillis() + millis);
        URL url = oss.generatePresignedUrl(bucketName, replacePath(path), expireTime);
        return url.toString();
    }

}
